﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TinyUrlShortener.cs" company="XamlNinja">
//   2011 Richard Griffin and Ollie Riches
// </copyright>
// <summary>
//    Tiny url shortener
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace WP7Contrib.Services.UrlShorteners
{
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Browser;
    using System.Reactive.Disposables;
    using System.Reactive.Linq;
    using Communications;
    using Logging;

    /// <summary>
    /// Uses tinyurl.com to shortener a user supplied url, if it fails it returns the original url
    /// </summary>
    public sealed class TinyUrlShortener : IShortenUrls
    {
        private const string FailedRequest = "Failed to execute HTTP GET request, message - '{0}'.";

        private readonly ILog log;
        private readonly IEncodeProperties propertyEncoder;
        
        public TinyUrlShortener()
            : this(new UrlEncoder(), new DebugLog())
        {
        }

        public TinyUrlShortener(IEncodeProperties propertyEncoder, ILog log)
        {
            this.propertyEncoder = propertyEncoder;
            this.log = log;
        }

        public IObservable<string> Shorten(string url)
        {
            if (string.IsNullOrEmpty(url))
            {
                return Observable.Return(url);
            }

            try
            {
                this.log.Write(string.Format("TinyUrlShortener: Url - '{0}'.", url));
                
                return Observable.Defer<string>(() => Observable.Create<string>(obs =>
                {
                    var disposable = new BooleanDisposable();

                    try
                    {
                        var requestUrl = string.Format("http://tinyurl.com/api-create.php?url={0}", this.propertyEncoder.Encode(url));
                        this.log.Write(string.Format("TinyUrlShortener: Tiny url - '{0}'.", requestUrl));

                        var request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(new Uri(requestUrl));
                        request.Method = "GET";
                        request.AllowAutoRedirect = true;
                        request.CookieContainer = new CookieContainer();
                        
                        Observable.FromAsyncPattern(request.BeginGetResponse, ar =>
                        {
                            try
                            {
                                var shortenedUrl = url;
                                if (disposable.IsDisposed)
                                {
                                    return;
                                }

                                using (var response = (HttpWebResponse)request.EndGetResponse(ar))
                                {
                                    if (response.StatusCode == HttpStatusCode.OK)
                                    {
                                        using (var stream = new StreamReader(response.GetResponseStream()))
                                        {
                                            shortenedUrl = stream.ReadToEnd();
                                            shortenedUrl = shortenedUrl.Trim('\n');

                                            if (shortenedUrl.ToLower() == "error")
                                            {
                                                shortenedUrl = url;
                                            }
                                        }
                                    }
                                }

                                this.log.Write(string.Format("TinyUrlShortener: Shortened url - '{0}'.", shortenedUrl));
                                obs.OnNext(shortenedUrl);
                                obs.OnCompleted();
                            }
                            catch (Exception exn)
                            {
                                var message = string.Format(FailedRequest, exn.Message);
                                this.log.Write(message);
                                obs.OnNext(url);
                                obs.OnCompleted();
                            }
                        })();
                    }
                    catch(Exception exn)
                    {
                        var message = string.Format(FailedRequest, exn.Message);
                        this.log.Write(message);
                        obs.OnNext(url);
                        obs.OnCompleted();
                    }

                    return disposable;
                }));
            }
            catch (Exception exn)
            {
                var message = string.Format("TinyUrlShortener: Failed to shorten url, message - '{0}'.", exn.Message);
                this.log.Write(message);
                return Observable.Return(url);
            }
        }
    }
}
